use proc_macro2::TokenStream;
use quote::quote;
use syn::DeriveInput;
use syn::Result;

pub fn expand(input: &mut DeriveInput) -> Result<TokenStream> {
    let handler_ty_ident = &input.ident;
    match &input.data {
        syn::Data::Struct(data_struct) => {
            let mut value_tokens = quote!();
            for field in &data_struct.fields {
                let field_ty = &field.ty;
                match &field.ident {
                    Some(field_ident) => {
                        value_tokens = quote! {
                            #value_tokens
                            #field_ident: <#field_ty as axum::extract::FromRequestParts<__AppState>>::from_request_parts(parts, state)
                                .await.map_err(|e| <__ExtractHandlerError as carpet::server::handler::ExtractHandlerError>::from_error(Box::new(e)))?,
                        }
                    }
                    None => {
                        value_tokens = quote! {
                            #value_tokens
                            <#field_ty as axum::extract::FromRequestParts<__AppState>>::from_request_parts(parts, state)
                                .await.map_err(|e| <__ExtractHandlerError as carpet::server::handler::ExtractHandlerError>::from_error(Box::new(e)))?,
                        }
                    }
                }
            }
            
            if let syn::Fields::Unnamed(_) = data_struct.fields {
                value_tokens = quote!(Self(#value_tokens));
            } else {
                value_tokens = quote!(Self {#value_tokens});
            }

            let expanded = quote! {
                impl axum::extract::FromRequestParts<__AppState> for #handler_ty_ident {
                    type Rejection = __ExtractHandlerError;

                    async fn from_request_parts(
                        parts: &mut axum::http::request::Parts,
                        state: &__AppState,
                    ) -> Result<Self, Self::Rejection> {
                        Ok(#value_tokens)
                    }
                }

            };
            Ok(expanded)
        }
        syn::Data::Enum(_) => Err(moss::error::Error::unexpected_type("enum").into()),
        syn::Data::Union(_) => Err(moss::error::Error::unexpected_type("union").into()),
    }
}
